#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <utmp.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>

struct ProcessInfo {
    int pid;
    char *user;
    int priority;
    int nice;
    long virt_memory;
    long res_memory;
    long shr_memory;
    char state;
    float cpu_usage;
    float mem_usage;
    char *time;
    char *command;
    struct ProcessInfo *next;
};

static void Print(struct ProcessInfo *process);

static unsigned long total_memory = 0;

int main()
{
    int i = 1;

    struct ProcessInfo *process = NULL;
    getProcessPid(&process);
    if(process != NULL){
        getProcessPri(process);
        getProcessMem(process);
        getProcessStat(process);
        getProcessCpu(process);
        getProcessCpu(process);
        sort_processes(&process);
        getHeadPrint(process);
        Print(process);
    }
    return 0;
}

//sort
int compare_process(const void *a, const void *b) {
    float cpu_usage_a = ((struct ProcessInfo *)b)->cpu_usage;
    float cpu_usage_b = ((struct ProcessInfo *)a)->cpu_usage;

    // 降序排序
    if (cpu_usage_a > cpu_usage_b) return -1;
    if (cpu_usage_a < cpu_usage_b) return 1;
    return 0;
}

void sort_processes(struct ProcessInfo **process) {
    struct ProcessInfo *sortedList = NULL;
    struct ProcessInfo *current = *process;

    while (current) {
        struct ProcessInfo *next = current->next;

        struct ProcessInfo **p = &sortedList;
        while (*p != NULL && compare_process(current, *p) <= 0) {
            p = &((*p)->next);
        }

        // 插入当前节点到已排序的链表
        
        current->next = *p;
        
        *p = current;

        current = next;
    }
    if(sortedList != NULL)
        *process = sortedList; // 更新原始链表的头指针
}
void  getHeadPrint(struct ProcessInfo *process)
{
    time_t currentTime;
    struct tm *localTimeInfo;

    // 获取当前时间
    (void)time(&currentTime);
    // 将时间转换为本地时间结构
    localTimeInfo = localtime(&currentTime);


    FILE *uptimeFile = fopen("/proc/uptime", "r");

    if (uptimeFile == NULL) {
        perror("无法打开/proc/uptime文件");
        return;
    }

    double uptime, idleTime;

    if (fscanf(uptimeFile, "%lf %lf", &uptime, &idleTime) != 2) {
        perror("读取/proc/uptime文件失败");
        (void)fclose(uptimeFile);
        return;
    }

    (void)fclose(uptimeFile);

    // 计算系统运行时间
    unsigned int seconds = (unsigned int)uptime;
    unsigned int minutes = seconds / 60;
    unsigned int hours = minutes / 60;

//用户数
    struct utmp *utmpEntry;
    int loggedInUsers = 0;
    
    setutent(); // 打开utmp文件
    
    while ((utmpEntry = getutent()) != NULL) {
        if (utmpEntry->ut_type == USER_PROCESS) {
            loggedInUsers++;
        }
    }
    endutent(); // 关闭utmp文件
    
    

    //平均负载
    FILE *loadavgFile;
    double loadavg_1min, loadavg_5min, loadavg_15min;
    int runningProcesses, totalProcesses;

    loadavgFile = fopen("/proc/loadavg", "r");

    if (loadavgFile == NULL) {
        perror("无法打开 /proc/loadavg 文件");
        return;
    }

    if (fscanf(loadavgFile, "%lf %lf %lf %d/%d", &loadavg_1min, &loadavg_5min, &loadavg_15min, &runningProcesses, &totalProcesses) != 5) {
        perror("读取 /proc/loadavg 文件失败");
        (void)fclose(loadavgFile);
        return;
    }

    (void)fclose(loadavgFile);

//进程状态信息
struct ProcessInfo *current = process;
int total_count = 0,run_count = 0,sleep_count = 0,stop_count = 0,zombie_count  = 0;
while(current != NULL){
    total_count++;
    if(current->state == 'R'){
        run_count++;
    }else if(current->state == 'S'){
        sleep_count++;
    }else if(current->state == 'T'){
        stop_count++;
    }else if(current->state == 'Z'){
        zombie_count++;
    }
    current = current->next;
}
//CPU状态信息
    FILE *statFile = fopen("/proc/stat", "r");
        if (statFile == NULL) {
            perror("无法打开 /proc/stat 文件");
            return;
        }

        char line[1024];

        while (fgets(line, (int)sizeof(line), statFile) != NULL) {
            if (strncmp(line, "cpu ", 4) == 0) {
                unsigned long long int user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice;
                (void)sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, &guest_nice);
                unsigned long long int total = user + nice + system + idle + iowait + irq + softirq + steal;
                (void)sleep(1);//计算一秒后的对应数据
                FILE *statFile_1 = fopen("/proc/stat", "r");
               if (statFile_1 == NULL) {
                  perror("无法打开 /proc/stat 文件");
                  return;
                }
                while (fgets(line, (int)sizeof(line), statFile_1) != NULL) {
                    if (strncmp(line, "cpu ", 4) == 0) {
                        unsigned long long int user1, nice1, system1, idle1, iowait1, irq1, softirq1, steal1, guest1, guest_nice1;
                        (void)sscanf(line + 5, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &user1, &nice1, &system1, &idle1, &iowait1, &irq1, &softirq1, &steal1, &guest1, &guest_nice1);
                        unsigned long long int total1 = user1 + nice1 + system1 + idle1 + iowait1 + irq1 + softirq1 + steal1;
                        printf("Top - %02d:%02d:%02d", localTimeInfo->tm_hour, localTimeInfo->tm_min, localTimeInfo->tm_sec);
                        printf(" up： %u :%02u",  hours,minutes%60);
                        printf("    %d user", loggedInUsers);
                        printf("    load average: %.2lf，%.2lf，%.2lf\n", loadavg_1min,loadavg_5min,loadavg_15min);
                        printf("task: %d total,   %d running, %d sleeping,   %d stopped,   %d zombie\n",total_count,run_count,total_count-run_count,stop_count,zombie_count);
                        printf("%%Cpu(s):  %.1f us,  %.1f sy,  %.1f ni,  %.1f id,  %.1f wa,  %.1f hi,  %.1f si,  %.1f st\n",
                       100.0 * (float)(user1-user) / (float)(total1-total), 100.0 * (float)(system1-system) / (float)(total1-total), 100.0 * (float)(nice1-nice) / (float)(total1-total),
                       100.0 * (float)(idle1-idle) / (float)(total1-total), 100.0 * (float)(iowait1-iowait) / (float)(total1-total), 100.0 * (float)(irq1-irq) / (float)(total1-total),
                       100.0 * (float)(softirq1-softirq) / (float)(total1-total), 100.0 * (float)(steal1-steal) / (float)(total1-total));


                      break;
                    }
                }

        (void)fclose(statFile_1);
                

                
            }
        }
        (void)fclose(statFile);

        FILE *meminfoFile = fopen("/proc/meminfo", "r");

    if (meminfoFile == NULL) {
        perror("无法打开 /proc/meminfo 文件");
        return;
    }

    char memline[1024];
    unsigned long  free_memory = 0, buffers_memory = 0, cached_memory = 0, swap_total = 0, swap_free = 0,available_memory = 0;

    while (fgets(memline, (int)sizeof(memline), meminfoFile) != NULL) {
        if (strstr(memline, "MemTotal:") != NULL) {
            (void)sscanf(memline, "MemTotal: %lu kB", &total_memory);
        } else if (strstr(memline, "MemFree:") != NULL) {
            (void)sscanf(memline, "MemFree: %lu kB", &free_memory);
        } else if (strstr(memline, "MemAvailable:") != NULL) {
            (void)sscanf(memline, "MemAvailable: %lu kB", &available_memory);
        } else if(strstr(memline, "Buffers:")){
            (void)sscanf(memline, "Cached: %lu kB", &buffers_memory);
        }else if (strstr(memline, "Cached:") != NULL) {
            (void)sscanf(memline, "Cached: %lu kB", &cached_memory);
        } else if (strstr(memline, "SwapTotal:") != NULL) {
            (void)sscanf(memline, "SwapTotal: %lu kB", &swap_total);
        } else if (strstr(memline, "SwapFree:") != NULL) {
            (void)sscanf(memline, "SwapFree: %lu kB", &swap_free);
        }
    }

    (void)fclose(meminfoFile);

    printf("KiB Mem：%.2f  total,   %.2f free,  %.2f used,  %.2f buff/cache\n", (double)total_memory/1024,(double)free_memory/1024,((double)total_memory - (double)free_memory - (double)buffers_memory - (double)cached_memory)/1024,((double)buffers_memory + (double)cached_memory)/1024);
    printf("KiB Swap：  %.2f total,   %.2f free,    %.2f used.  %.2f avail Mem\n", (double)swap_total/1024,(double)swap_free/1024,((double)swap_total - (double)swap_free)/1024,(double)available_memory/1024);

}

void startTime(long long int time, char **result)
{
    // 解析 time，将其转换为可读的格式,秒数按60
    int hours, minutes, seconds;
    hours = (int)(time / 3600);
    minutes = (int)((time - hours * 3600) / 60);
    seconds = (int)(time - hours * 3600 - minutes * 60);
    // 分配内存来存储时间字符串
    char *ptime = (char *)malloc(16);
    if(ptime != NULL){
        int  res = snprintf(ptime, 16, "%01d:%02d.%02d", hours, minutes, seconds);
        if(res< 0){
             fprintf(stderr, "Error.\n");
        }
    }
    if(ptime != NULL)
        *result = ptime;
}

 void getProcessPid(struct ProcessInfo **process)
{
    // 获取进程列表
        
    DIR *procdir = opendir("/proc");
    if (procdir == NULL)
    {
        perror("Failed to open /proc directory");
        exit(EXIT_FAILURE);
    }
    struct dirent *entry;
    while ((entry = readdir(procdir)) != NULL)
    {
        char path[512];
        int re = snprintf(path, sizeof(path), "/proc/%s", entry->d_name);
        if(re< 0){
            fprintf(stderr, "Error.\n");
       }
        struct stat statbuf;
        memset(&statbuf, 0, sizeof(struct stat));
        if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
        {
            // 如果路径对应的是一个目录，则可以认为是进程目录
            int pid = atoi(entry->d_name);
            if (pid > 0)
            {
                // 分配内存来存储新的进程信息
                struct ProcessInfo *newProcess = (struct ProcessInfo *)malloc(sizeof(struct ProcessInfo));
                if (newProcess != NULL) {
                    memset(newProcess, 0, sizeof(struct ProcessInfo)); // 初始化结构体为0
                }

                
                
                if (newProcess != NULL) {
                    newProcess->pid = pid;

                // 打开进程的状态文件,获取UID以对比用户名，获取命令名
                char statusPath[512];
                int res = snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid);
                if(res< 0){
                    fprintf(stderr, "Error.\n");
                }
                FILE *statusFile = fopen(statusPath, "r");
                if (statusFile != NULL)
                {
                    char line[256];
                    while (fgets(line, (int)sizeof(line), statusFile))
                    {
                        // 查找Uid行
                        if (strstr(line, "Uid:") == line)
                        {
                            // 获取UID
                            uid_t euid, ruid;
                            if (sscanf(line, "Uid:\t%u\t%u", (unsigned int*)&euid, (unsigned int*)&ruid) == 2)
                            {
                                if (euid == 0 || ruid == 0)
                                {
                                    free(newProcess->user);
                                    
                                    newProcess->user = strdup("root");
                                }
                                else
                                {
                                        
                                    struct passwd *pwd = getpwuid(euid);
                                    if (pwd != NULL)
                                    {
                                        // 分配内存来存储用户名
                                        if (newProcess->user != NULL) {
                                            free(newProcess->user);
                                        }
                                        newProcess->user = strdup(pwd->pw_name);
                                        
                                    }
                                }
                            }
                            break;
                        }
                        else if (strstr(line, "Name:") == line)
                        {
                            char cmd[256];
                            if (sscanf(line, "Name:\t%255s", cmd) == 1)
                            {
                                if (newProcess->command != NULL) {
                                    free(newProcess->command);
                                }
                                newProcess->command = strdup(cmd);
                                // 使用 strdup 函数来分配动态内存并复制命令名称
                            }
                        }
                    }
                }
                // 打开进程的stat文件,获取时间
                char statPath[512];
                int ret = snprintf(statPath, sizeof(statPath), "/proc/%d/stat", pid);
                if(ret< 0){
                    fprintf(stderr, "Error.\n");
                }
                FILE *statFile = fopen(statPath, "r");
                if (statFile != NULL)
                {
                    unsigned long int utime, stime;
                    (void)fscanf(statFile, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %lu %lu",&utime, &stime);
                    {
                        startTime((long long)utime + stime, &newProcess->time);
                    }
                    (void)fclose(statFile);
                }

                // 将新的进程信息添加到链表中
                if(newProcess->next != NULL){
                    free(newProcess->next);
                    newProcess->next = NULL;
                }
                if (*process == NULL)
                {
                    *process = newProcess;
                }
                else
                {
                    // 找到链表的尾部
                    struct ProcessInfo *tail = *process;
                    while (tail->next != NULL)
                    {
                        tail = tail->next;
                    }
                    tail->next = newProcess;
                }
                if(statusFile != NULL)
                    (void)fclose(statusFile);
            }
            }
        }
    }
    
    (void)closedir(procdir);
    if (*process == NULL || (*process)->next == NULL) {
       exit(EXIT_FAILURE);
    }
}


void getProcessPri(struct ProcessInfo *process)
{
        
    DIR *procdir = opendir("/proc");
    if (procdir == NULL)
    {
        perror("Failed to open /proc directory");
        exit(EXIT_FAILURE);
    }

    struct dirent *entry;
    struct ProcessInfo *processpid = process;
    while ((entry = readdir(procdir)) != NULL)
    {
        char path[512];
        int res = snprintf(path, sizeof(path), "/proc/%s", entry->d_name);
        if(res < 0){
          fprintf(stderr, "Error.\n");
        }
        struct stat statbuf;
        memset(&statbuf, 0, sizeof(struct stat));
        if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
        {
            int pid = atoi(entry->d_name);
            if (pid > 0)
            {
                while (processpid != NULL)
                {
                    if (processpid->pid == pid)
                    {
                        // 打开/proc/[PID]/stat文件以读取PR和NI值
                        char stat_path[512];
                        int ret = snprintf(stat_path, sizeof(stat_path), "/proc/%d/stat", pid);
                        if(ret< 0){
                             fprintf(stderr, "Error.\n");
                        }
                        FILE *stat_file = fopen(stat_path, "r");
                        if (stat_file != NULL)
                        {
                            // 读取stat文件中的数据
                            char stat_buffer[1024];
                            if (fgets(stat_buffer, (int)sizeof(stat_buffer), stat_file) != NULL)
                            {
                                (void)sscanf(stat_buffer, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*u %*d %*d %*d %d %d", &(processpid->priority), &(processpid->nice));
                            }

                            (void)fclose(stat_file);
                        }
                        process = processpid;
                        break;
                    }
                    processpid = processpid->next;
                }
            }
        }
    }

    (void)closedir(procdir);
}


//获取共享内存SHR和进程状态S
void getProcessStat(struct ProcessInfo *process) {
    
    DIR *procdir = opendir("/proc");
    if (procdir == NULL)
    {
        perror("Failed to open /proc directory");
        exit(EXIT_FAILURE);
    }

    struct dirent *entry;
    struct ProcessInfo *processpid = process;
    while ((entry = readdir(procdir)) != NULL)
    {
        char path[512];
        int ret = snprintf(path, sizeof(path), "/proc/%s", entry->d_name);
        if(ret< 0){
            fprintf(stderr, "Error.\n");
       }
        struct stat statbuf;
        memset(&statbuf, 0, sizeof(struct stat));
        if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
        {
            int pid = atoi(entry->d_name);
            if (pid > 0)
            {
                while (processpid != NULL)
                {
                    if (processpid->pid == pid)
                    {
                        char status_path[512];
                        int res = snprintf(status_path, sizeof(status_path), "/proc/%d/status", pid);
                        if(res < 0){
                            fprintf(stderr, "Error.\n");
                        }
                        FILE *status_file = fopen(status_path, "r");
                        if (status_file != NULL)
                        {
                             char line[256];
                            while (fgets(line, (int)sizeof(line), status_file))
                            {
                                 // 查找State行
                                if (strstr(line, "State:") == line)
                                {
                                  // 获取State
                                  char state;
                                  if (sscanf(line, "State:\t%c", &state) == 1)
                                  {
                                      processpid->state = state;
                                  }
                                }
                                //查找RssShmem行
                                else if(strstr(line, "RssFile:") == line){
                                    // 获取RssShmem
                                  long shr = 0;
                                  if (sscanf(line, "RssFile:\t%ld", &shr) == 1)
                                  {
                                      processpid->shr_memory = shr;
                                  }

                                }
                            }

                            (void)fclose(status_file);
                        }
                        process = processpid;
                        break;
                    }
                    processpid = processpid->next;
                }
            }
        }
    }

    (void)closedir(procdir);

}


void getProcessMem(struct ProcessInfo *process) {
        
    DIR *procdir = opendir("/proc");
    if (procdir == NULL)
    {
        perror("Failed to open /proc directory");
        exit(EXIT_FAILURE);
    }

    struct dirent *entry;
    struct ProcessInfo *processpid = process;
    while ((entry = readdir(procdir)) != NULL)
    {
        char path[512];
        int res = snprintf(path, sizeof(path), "/proc/%s", entry->d_name);
        if(res < 0){
           fprintf(stderr, "Error.\n");
        }
        struct stat statbuf;
        memset(&statbuf, 0, sizeof(struct stat));
        if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
        {
            int pid = atoi(entry->d_name);
            if (pid > 0)
            {
                while(processpid != NULL){
                if(processpid->pid == pid){
                // 打开/proc/[PID]/stat文件以读取PR和NI值
                char status_path[512];
                int ret = snprintf(status_path, sizeof(status_path), "/proc/%d/status", pid);
                if(ret< 0){
                    fprintf(stderr, "Error.\n");
                }
                FILE *status_file = fopen(status_path, "r");
                if (status_file != NULL)
                {
                    char line[256];
    long vmSize = 0;
    long vmHWM = 0;

    while (fgets(line, (int)sizeof(line), status_file) != NULL)
    {
        // 查找包含 "VmSize" 和 "VmHWM" 的行
        if (strstr(line, "VmSize:") != NULL)
        {
            (void)sscanf(line, "VmSize:\t%ld kB", &vmSize);
        }
        else if (strstr(line, "VmRSS:") != NULL)
        {
            (void)sscanf(line, "VmRSS:\t%ld kB", &vmHWM);
        }
    }

    // 存储提取的值到 virt_memory 和 res_memory
    if (vmSize > 0 || vmSize == 0 )
    {
        processpid->virt_memory = vmSize;
    }
    else
    {
        fprintf(stderr, "Failed to parse VmSize value\n");
    }

    if (vmHWM > 0 || vmHWM == 0)
    {
        processpid->res_memory = vmHWM;
    }
    else
    {
        fprintf(stderr, "Failed to parse VmHWM value\n");
    }
                        (void)fclose(status_file);
                }
                        process = processpid;
                        break;
                    }
                    processpid = processpid->next;
                    
                    
                }

            }
        }
    }

    (void)closedir(procdir);
    
}



void getProcessCpu(struct ProcessInfo *process) {
    
    DIR *procdir = opendir("/proc");
    if (procdir == NULL) {
        perror("Failed to open /proc directory");
        exit(EXIT_FAILURE);
    }

    struct dirent *entry;
    struct ProcessInfo *processpid = process;
    while ((entry = readdir(procdir)) != NULL) {
        char path[512];
        int res = snprintf(path, sizeof(path), "/proc/%s", entry->d_name);
        if(res < 0){
           fprintf(stderr, "Error creating stat_path or path is too long.\n");
        }
        struct stat statbuf;
        memset(&statbuf, 0, sizeof(struct stat));
        if (stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
            int pid = atoi(entry->d_name);
            if (pid > 0) {
                while (processpid != NULL) {
                    if (processpid->pid == pid) 
                    {
                            char status_path[512];
                            int ret = snprintf(status_path, sizeof(status_path), "/proc/%d/status", pid);
                            if(ret < 0){
                                fprintf(stderr, "Error\n");
                            }
                            FILE *status_file = fopen(status_path, "r");
                            if (status_file == NULL)
                            {
                                perror("Failed to open status file");
                                exit(EXIT_FAILURE);
                            }
                            long vm_size = 0, vm_rss = 0;
                            char line[256];
                            while (fgets(line, (int)sizeof(line), status_file))
                            {
                                if (strstr(line, "VmSize:") == line)
                                {
                                    (void)sscanf(line, "VmSize:\t%ld kB", &vm_size);
                                    }
                                    else if (strstr(line, "VmRSS:") == line)
                                    {
                                        (void)sscanf(line, "VmRSS:\t%ld kB", &vm_rss);
                                    }
                            }
                                            if (vm_size > 0)
                                            {
                                                        processpid->mem_usage = ((float)vm_rss /(float)total_memory) * 100 ;
                                            }
                                            else
                                            {
                                                processpid->mem_usage = 0.0;
                                            }
                                            (void)fclose(status_file);
                                            //获取cpu使用率
                                            char stat_path[512];
                                            
                                            int result = snprintf(stat_path, sizeof(stat_path), "/proc/%d/stat", pid);
                                            if(result < 0){
                                                fprintf(stderr, "Error creating stat_path or path is too long.\n");
                                            }
                                            FILE *stat_file = fopen(stat_path, "r");
                                            if (stat_file == NULL)
                                            {
                                                perror("Failed to open stat file");
                                                 exit(EXIT_FAILURE);
                                            }
                                            unsigned long utime, stime;
                                            if (fscanf(stat_file, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu %lu %lu", &utime, &stime) == 2)
                                            {
                                                unsigned long total_time = utime + stime;
                                                float cpu_usage = ((float)total_time / 10);
                                                processpid->cpu_usage = cpu_usage - processpid->cpu_usage;
                                            }
                                            (void)fclose(stat_file);
                                            process = processpid; 
                                            break;
                                            
                    } 
                        processpid = processpid->next;
                    }
                }
            }
    }
        (void)closedir(procdir);
}


void Print(struct ProcessInfo *process)
{
    struct ProcessInfo *current = process;
    printf("PID \tUSER \tPR \tNI \tVIRT \tRES \tSHR \tS \t%%CPU \t%%MEM \tTIME+ \t\tCOMMAND\n");
    int count = 20;
    while (current != NULL && count != 0)
    {
        count--;
        printf("%d\t", current->pid);
        printf("%s\t", current->user);
        if(current->priority == -100){
            printf("rt\t");
        }else{
            printf("%d\t", current->priority);
        }
        printf("%d\t", current->nice);
        if( current->virt_memory > 1024*1024*1024){
            printf("%.3ft\t", (double)current->virt_memory/(1024*1024*1024));
        }else if(current->virt_memory > 9999999){
            printf("%.3fg\t", (double)current->virt_memory/(1024*1024));
        }else{
            printf("%ld\t",current->virt_memory);
        }
        printf("%ld\t", current->res_memory);
        printf("%ld\t", current->shr_memory);
        printf("%c\t", current->state);
        printf("%.1f\t", current->cpu_usage);
        printf("%.1f\t", current->mem_usage);
        printf("%s\t\t", current->time);
        printf("%s\t", current->command);
        printf("\n");

        current = current->next;
    }
}
